/*
 * @(#)RL_Rule.java        1.0 2000/05/09
 *
 * This file is part of Xfuzzy 3.0, a design environment for fuzzy logic
 * based systems.
 *
 * (c) 2000 IMSE-CNM. The authors may be contacted by the email address:
 *                    xfuzzy-team@imse.cnm.es
 *
 * Xfuzzy is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation.
 *
 * Xfuzzy is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
//REGLA DE UNA LISTA DE REGLAS				//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//

package xfuzzy.lang;

/**
 * 
 * Clase que describe una regla de una lista de reglas difusas
 * 
 * @author Francisco Jos� Moreno Velo
 * @author Pablo Martin Mu�oz
 *
 */
public class RL_Rule {

	//----------------------------------------------------------------------------//
	//                            MIEMBROS PRIVADOS                              //
	//----------------------------------------------------------------------------//

	public static final int FIRST_RULE = 0;
	public static final int INNER_RULE = 1;
	public static final int LAST_RULE = 2;
	
	/**
	 * Antecedente de la regla
	 */
	private Relation premise;
	
	/**
	 * Lista de consecuentes de la regla
	 */
	private Conclusion conclusion[];
	
	/**
	 * Tipo de regla de la lista
	 */
	private int type;
	
	/**
	 * M�ximo grado de activaci�n alcanzado por la regla
	 */
	private double maxactivation = 0;
	
	/**
	 * Indicador de si la regla tiene componentes con par�metros a ajustar
	 */
	private boolean adjustable = true;

	//----------------------------------------------------------------------------//
	//                                CONSTRUCTOR                                 //
	//----------------------------------------------------------------------------//

	/**
	 * Constructor basado en el antecedente
	 */
	public RL_Rule(Relation premise, int type) {
		this.premise = premise;
		this.conclusion = new Conclusion[0];
		this.type = type;
	}

	//----------------------------------------------------------------------------//
	//                             M�TODOS P�BLICOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * Obtiene un duplicado del objeto
	 */
	public Object clone(KnowledgeBase rb) {
		Relation premiseclone = (premise == null? null : (Relation) premise.clone(rb));
		RL_Rule clone = new RL_Rule(premiseclone,this.type);
		for(int i=0; i<conclusion.length; i++)
			clone.add( (Conclusion) conclusion[i].clone(rb) );
		return clone;
	}

	/**
	 * Elimina los enlaces de los miembros para borrar la regla
	 */
	public void dispose() {
		if(premise != null) this.premise.dispose();
		for(int i=0; i<this.conclusion.length; i++) this.conclusion[i].dispose();
	}

	/**
	 * Obtiene el antecedente de la regla
	 */
	public Relation getPremise() {
		return this.premise;
	}

	/**
	 * Obtiene la lista de consecuentes de la regla
	 */
	public Conclusion[] getConclusions() {
		return this.conclusion;
	}
	
	/**
	 * Obtiene el tipo de la regla
	 */
	public int getType() {
		return this.type;
	}
	
	/**
	 * Define el tipo de la regla
	 */
	public void setType(int t) {
		this.type = t;
	}

	/**
	 * Intercambia dos funciones de pertenencia en la regla
	 */
	public void exchange(LinguisticLabel oldmf, LinguisticLabel newmf) {
		if(premise != null) premise.exchange(oldmf,newmf);
		for(int i=0; i<conclusion.length; i++) conclusion[i].exchange(oldmf,newmf);
	}

	/**
	 * Intercambia dos variables en la regla
	 */
	public void exchange(Variable oldvar, Variable newvar) {
		if(premise != null) premise.exchange(oldvar,newvar);
		for(int i=0; i<conclusion.length; i++) conclusion[i].exchange(oldvar,newvar);
	}

	/**
	 * A�ade un consecuente a la regla	
	 */
	public void add(Conclusion ncc) {
		Conclusion acc[] = new Conclusion[this.conclusion.length+1];
		System.arraycopy(this.conclusion,0,acc,0,this.conclusion.length);
		acc[this.conclusion.length] = ncc;
		this.conclusion = acc;
	}

	//----------------------------------------------------------------------------//
	// M�todos que generan c�digo                                                 //
	//----------------------------------------------------------------------------//

	/**
	 * Genera la descripci�n XFL3 de la regla
	 */
	public String toXfl() {
		String eol = System.getProperty("line.separator", "\n");
		String code = "  ";
		if (this.type == FIRST_RULE) {
			//IF
			code += "if(";
			code += this.premise.toXfl();
			code += ") -> ";
		}
		else if (this.type == INNER_RULE) {
			//ELSEIF
			code += "elseif(";
			code += this.premise.toXfl();
			code += ") -> ";
		}
		else {
			//ELSE
			code += "else ";
		}
		code += this.conclusion[0].toXfl();
		for(int i=1; i<this.conclusion.length; i++) 
			code += ", "+this.conclusion[i].toXfl();
		code += ";"+eol;
		return code;
	}

	//----------------------------------------------------------------------------//
	// M�todos de ejecuci�n de la inferencia                                      //
	//----------------------------------------------------------------------------//

	/**
	 * Ejecuta la inferencia de la regla
	 */
	public double compute(double val) throws XflException {
		double activation;
		double mu;
		if (this.type == LAST_RULE) {
			//Tipo de regla: else
			mu = 1.0;
		}
		else {
			//Tipo de regla: if o elseif
			mu = this.premise.compute();
		}
		activation = val * mu;
		if(activation > maxactivation ) maxactivation = activation;
		for(int i=0; i<this.conclusion.length; i++)
			this.conclusion[i].compute(activation);
		return activation;
	}

	/**
	 * Calcula la derivada de los miembros del antecedente
	 */
	public void derivative() throws XflException {
		if(!adjustable) return;
		double deriv = 0;
		for(int i=0; i<this.conclusion.length; i++)
			deriv += this.conclusion[i].getDegreeDeriv();
		if(deriv != 0 && premise != null) this.premise.derivative(deriv);
	}

	/**
	 * Inicializa el grado de activaci�n m�ximo
	 */
	public void resetMaxActivation() {
		this.maxactivation = 0;
	}

	/**
	 * Obtiene el grado de activaci�n m�ximo
	 */
	public double getMaxActivation() {
		return this.maxactivation;
	}

	/**
	 * Analiza si la regla es ajustable
	 */
	public void setAdjustable() {
		if(premise != null) this.adjustable = this.premise.isAdjustable();
		else this.adjustable = false;
	}

	/**
	 * Verifica si la regla es ajustable
	 */
	public boolean isAdjustable() {
		return this.adjustable;
	}
}
